#include "push_curl.h"

map<long, map<string, scaler *>> msc;

void init_scaler_map(long addr)
{
    if (msc.count(addr) <= 0)
    {
        scaler *total = new scaler;
        scaler *second = new scaler;
        msc[addr]["total"] = total;
        msc[addr]["second"] = second;
    }
}

void push_curl_init_handler(zend_execute_data *execute_data, zval *return_value, long execute_micro_time, string class_name, string function_name)
{
    // orig_curl_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
}

void push_curl_setopt_handler(zend_execute_data *execute_data, zval *return_value, long execute_micro_time, string class_name, string function_name)
{
    zval *zid, *zvalue;
    zend_long options;
    std::string method;
    std::string item_name = ITEM_HTTP_METHOD;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &zid, &options, &zvalue) == FAILURE)
    {
        return;
    }

    if (PUSH_CURLOPT_POST == options)
    {
        switch (Z_TYPE_P(zvalue))
        {
        case IS_TRUE:
            method = VALUE_HTTP_METHOD_POST;
            break;
        case IS_FALSE:
            method = VALUE_HTTP_METHOD_GET;
            break;
        case IS_STRING:
            convert_to_long(zvalue);
        case IS_LONG:
            method = Z_LVAL_P(zvalue) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        default:
            method = VALUE_HTTP_METHOD_GET;
            break;
        }
    }
    else
    {
        method = VALUE_HTTP_METHOD_GET;
    }

    // 获取函数传参
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        zval *ch = ZEND_CALL_ARG(execute_data, 1);
        long addr = reinterpret_cast<long>(ch);

        init_scaler_map(addr);

        msc[addr]["total"]->addItem(item_name, method);
        msc[addr]["second"]->addItem(item_name, method);
    }
}

void push_curl_setopt_array_handler(zend_execute_data *execute_data, zval *return_value, long execute_micro_time, string class_name, string function_name)
{
    zval *zid, *arr;
    std::string method;
    std::string item_name = ITEM_HTTP_METHOD;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &zid, &arr) == FAILURE)
    {
        return;
    }

    zval *zvalue = zend_hash_index_find(Z_ARRVAL_P(arr), PUSH_CURLOPT_POST);
    if (zvalue != nullptr)
    {
        switch (Z_TYPE_P(zvalue))
        {
        case IS_TRUE:
            method = VALUE_HTTP_METHOD_POST;
            break;
        case IS_FALSE:
            method = VALUE_HTTP_METHOD_GET;
            break;
        case IS_STRING:
            convert_to_long(zvalue);
        case IS_LONG:
            method = Z_LVAL_P(zvalue) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        default:
            method = VALUE_HTTP_METHOD_GET;
            break;
        }
    }
    else
    {
        method = VALUE_HTTP_METHOD_GET;
    }

    // 获取函数传参
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        zval *ch = ZEND_CALL_ARG(execute_data, 1);
        long addr = reinterpret_cast<long>(ch);

        init_scaler_map(addr);

        msc[addr]["total"]->addItem(item_name, method);
        msc[addr]["second"]->addItem(item_name, method);
    }
}

void push_curl_exec_handler(zend_execute_data *execute_data, zval *return_value, long execute_micro_time, string class_name, string function_name)
{

    zval *zid;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE)
    {
        return;
    }

    // 执行系统 curl_getinfo 函数
    zval func;
    zval args[1];
    zval curl_info;
    ZVAL_COPY(&args[0], zid);
    ZVAL_STRING(&func, "curl_getinfo");
    call_user_function(CG(function_table), NULL, &func, &curl_info, 1, args);
    zval_dtor(&func);
    zval_dtor(&args[0]);

    zval *z_url = zend_hash_str_find(Z_ARRVAL(curl_info), ZEND_STRL("url"));
    zval *z_http_code = zend_hash_str_find(Z_ARRVAL(curl_info), ZEND_STRL("http_code"));
    zval *z_total_time = zend_hash_str_find(Z_ARRVAL(curl_info), ZEND_STRL("total_time"));
    zval *z_primary_ip = zend_hash_str_find(Z_ARRVAL(curl_info), ZEND_STRL("primary_ip"));
    zval *z_primary_port = zend_hash_str_find(Z_ARRVAL(curl_info), ZEND_STRL("primary_port"));

    std::string http_code_str = z_http_code == nullptr ? "unknown" : to_string(Z_LVAL_P(z_http_code));
    std::string total_time_str = z_total_time == nullptr ? "unknown" : doubleToString(Z_DVAL_P(z_total_time), 6);
    std::string primary_ip_str = z_primary_ip == nullptr ? "unknown" : Z_STRVAL_P(z_primary_ip);
    std::string primary_port_str = z_primary_port == nullptr ? "unknown" : to_string(Z_LVAL_P(z_primary_port));

    // 执行系统 parse_url 函数
    zval url_info;
    zval url_args[1];
    ZVAL_COPY(&url_args[0], z_url);
    ZVAL_STRING(&func, "parse_url");
    call_user_function(CG(function_table), NULL, &func, &url_info, 1, url_args);
    zval_dtor(&func);
    zval_dtor(&url_args[0]);

    zval *z_url_scheme = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("scheme"));
    zval *z_url_host = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("host"));
    zval *z_url_port = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("port"));
    zval *z_url_path = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("path"));
    zval *z_url_query = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("query"));

    std::string url_scheme = z_url_scheme == nullptr ? "" : Z_STRVAL_P(z_url_scheme);
    std::string url_host = z_url_host == nullptr ? "" : Z_STRVAL_P(z_url_host);
    std::string url_port = z_url_port == nullptr ? "" : to_string(Z_LVAL_P(z_url_port));
    std::string url_path = z_url_path == nullptr ? "" : Z_STRVAL_P(z_url_path);
    std::string url_query = z_url_query == nullptr ? "" : Z_STRVAL_P(z_url_query);
    std::string url_full_path = z_url_query == nullptr ? url_path : url_path + "?" + url_query;

    if (url_port == "")
    {
        if (url_scheme == "https")
        {
            url_port = "443";
        }
        else if (url_scheme == "http")
        {
            url_port = "80";
        }
    }

    // 获取函数传参
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        zval *ch = ZEND_CALL_ARG(execute_data, 1);
        long addr = reinterpret_cast<long>(ch);

        init_scaler_map(addr);

        msc[addr]["second"]->addItem(ITEM_HTTP_ADDRESS, primary_ip_str + ":" + primary_port_str);
        msc[addr]["second"]->addItem(ITEM_HTTP_FULL_PATH, url_full_path);
        msc[addr]["second"]->addItem(ITEM_HTTP_PATH, url_path);
        msc[addr]["second"]->addItem(ITEM_HTTP_PEER, url_host + ":" + url_port);
        msc[addr]["second"]->setIndex(INDEX_HTTP_CLIENT_HANDLING_SECCONDS);

        msc[addr]["total"]->addItem(ITEM_HTTP_CODE, http_code_str);
        msc[addr]["total"]->addItem(ITEM_HTTP_ADDRESS, primary_ip_str + ":" + primary_port_str);
        msc[addr]["total"]->addItem(ITEM_HTTP_FULL_PATH, url_full_path);
        msc[addr]["total"]->addItem(ITEM_HTTP_PATH, url_path);
        msc[addr]["total"]->addItem(ITEM_HTTP_PEER, url_host + ":" + url_port);
        msc[addr]["total"]->setIndex(INDEX_HTTP_CLIENT_HANDLED_TOTAL);

        client *pushgateway_client = client::newInstance();
        string bucket = PUSHGATEWAY_G(bucket);
        pushgateway_client->setBucket(msc[addr]["second"], Z_DVAL_P(z_total_time), bucket);
        pushgateway_client->setCounter(msc[addr]["total"]);
    }
}

void push_curl_close_handler(zend_execute_data *execute_data, zval *return_value, long execute_micro_time, string class_name, string function_name)
{
    // 获取函数传参
    uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
    if (arg_count)
    {
        zval *ch = ZEND_CALL_ARG(execute_data, 1);
        long addr = reinterpret_cast<long>(ch);
        msc.erase(addr);
    }
}